package com.fiona.core.protocol;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.fiona.core.message.RpcRequest;
import com.fiona.core.message.RpcResponse;

import cn.hutool.core.exceptions.ExceptionUtil;

/**
 * Kryo serialization class, Kryo serialization efficiency is very high, but
 * only compatible with Java language
 *
 * @author huacongnan
 * @createTime 2020年05月13日 19:29:00
 */
public class KryoMessageProtocol implements MessageProtocol {

	/**
	 * Because Kryo is not thread safe. So, use ThreadLocal to store Kryo objects
	 */
	private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
		Kryo kryo = new Kryo();
		kryo.register(RpcResponse.class);
		kryo.register(RpcRequest.class);
		return kryo;
	});

	@Override
	public byte[] serialize(Object obj) {
		try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
				Output output = new Output(byteArrayOutputStream)) {
			Kryo kryo = kryoThreadLocal.get();
			// Object->byte:将对象序列化为byte数组
			kryo.writeObject(output, obj);
			kryoThreadLocal.remove();
			return output.toBytes();
		} catch (Exception e) {
			throw ExceptionUtil.wrapRuntime(e);
		}
	}

	@Override
	public <T> T deserialize(byte[] bytes, Class<T> clazz) {
		try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
				Input input = new Input(byteArrayInputStream)) {
			Kryo kryo = kryoThreadLocal.get();
			// byte->Object:从byte数组中反序列化出对对象
			Object o = kryo.readObject(input, clazz);
			kryoThreadLocal.remove();
			return clazz.cast(o);
		} catch (Exception e) {
			throw ExceptionUtil.wrapRuntime(e);
		}
	}

}
